<%@ page pageEncoding="UTF-8"%>
<%@ page import="org.apache.http.HttpEntity"%>
<%@ page import="org.apache.http.HttpResponse"%>
<%@ page import="org.apache.http.client.HttpClient"%>
<%@ page import="org.apache.http.client.entity.UrlEncodedFormEntity"%>
<%@ page import="org.apache.http.client.methods.HttpPost"%>
<%@ page import="org.apache.http.entity.StringEntity"%>
<%@ page import="org.apache.http.message.BasicNameValuePair"%>
<%@ page import="org.apache.http.util.EntityUtils"%>
<%@ page import="org.apache.http.conn.ClientConnectionManager"%>
<%@ page import="org.apache.http.conn.scheme.Scheme"%>
<%@ page import="org.apache.http.conn.scheme.SchemeRegistry"%>
<%@ page import="org.apache.http.impl.client.DefaultHttpClient"%>
<%@ page import="org.apache.http.conn.ssl.SSLSocketFactory"%>
<%@ page import="java.lang.reflect.*"%>
<%@ page import="com.google.gson.annotations.SerializedName"%>
<%@ page import="javax.net.ssl.*"%>
<%@ page import="java.security.cert.*"%>

<%!
	// 微信付款
	public final String charset = "utf-8";
	public final String signType = "MD5";
	public final String tradeType = "JSAPI";
	public final String createOrder = "https://api.mch.weixin.qq.com/pay/unifiedorder";   // 下单
	public final String refund = "https://api.mch.weixin.qq.com/secapi/pay/refund";       // 退款
	public final String queryOrder = "https://api.mch.weixin.qq.com/pay/orderquery";      // 查询订单
	public final String refund_query = "https://api.mch.weixin.qq.com/pay/refundquery";   // 退款查询
	public final String close= "https://api.mch.weixin.qq.com/pay/closeorder";            // 关闭订单
	public final String downloadbill= "https://api.mch.weixin.qq.com/pay/downloadbill";   // 下载账单

	// 存放数据库的字段
	public String partnerKey = "Suzhou510tianxiabu1TXBYwechatPay";
	public String appId = "wxd77ad63f47f83d46";
	public String mchId = "1229361102";
	public String notifyUrl = "http://demoapi.tianxiabuyi.com:18088/section/1057/service/notify.jsp";

	public String getNonceStr() { // 获得随机数
		Random random = new Random();
		return Tools.getMD5(String.valueOf(random.nextInt(10000))).toUpperCase();
	}

	public String getTimeStamp() { // 获得时间戳
		return String.valueOf(System.currentTimeMillis() / 1000);
	}

	public String getOrder(int rlength) {    // 随机生成商品号
		String result = "";
		try {
			Method methord = getClass().getMethod("orderId", new Class[]{int.class});    // 反射orderId方法如果不写该方法通过默认规则生成商品号
			if (methord != null) {
				result = (String) methord.invoke(this, rlength);
			}
		} catch (Exception e) {
			Random random = new Random();
			for (int i = 0; i < 10; i++) {
				result += random.nextInt(10);
			}
			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
			result += sdf.format(new Date(System.currentTimeMillis()));
		}
		return result;
	}

	public String getRefundOrder() {	 // 随机生成退款单号
		Random random = new Random();
		String result = "";
		for (int i = 0; i < 10; i ++) {
			result += random.nextInt(10);
		}
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		result = sdf.format(new Date(System.currentTimeMillis())) + result;
		return result;
	}

	public String getSign(Map<String,Object> map) { // 签名方法
        ArrayList<String> list = new ArrayList<String>(); // 循环整个Map
        for(Map.Entry<String,Object> entry:map.entrySet()){
            if(!Tools.getDesc(entry.getValue() + "", "").equals("")){   // Object判断有误 已修改
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + partnerKey;
        result = Tools.getMD5(result, charset).toUpperCase();
        return result;
    }

	public String getSign(Object o) {   // 签名方法
        ArrayList<String> list = new ArrayList<String>();
        Class cls = o.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
			try {
				if (f.get(o) != null && f.get(o) != "") {
					if (f.getAnnotation(SerializedName.class) != null) {
						String key = f.getAnnotation(SerializedName.class).value();
						list.add(key + "=" + f.get(o) + "&");
					} else {
						list.add(f.getName() + "=" + f.get(o) + "&");
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + partnerKey;
        result = Tools.getMD5(result, charset).toUpperCase();
        return result;
    }

	public String getSign(SortedMap<String, String> param) {  // 签名方法
		StringBuffer sb = new StringBuffer();
		Set paramSet = param.entrySet();
		Iterator iter = paramSet.iterator();
		while (iter.hasNext()) {
			Map.Entry<String, String> entry = (Map.Entry<String, String>)iter.next();
			String k = entry.getKey();
			String v = entry.getValue();
			if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + partnerKey);
		String sign = Tools.getMD5(sb.toString()).toUpperCase();
		return sign;
	}

	public boolean checkSign(String respStr) { // 检验返回的签名
        Map<String,Object> map = XmlUtil.dom2Map(XmlUtil.parse(respStr));
		String respSign = Tools.getDesc(map.get("sign") + "", "");
        if(respSign == "" || respSign == null){
            return false;
        }
        map.put("sign", "");
        String sign = getSign(map);
        if(!sign.equals(respSign)){
            return false;
        }
        return true;
    }

	public String _queryOrder(WxPayReqData reqData) {
	   String nonceStr = getNonceStr();
	   reqData.setNonceStr(nonceStr);
	   String sign = getSign(reqData);
	   String trade =  (null == reqData.getTransactionId() || "".equals(reqData.getTransactionId())) ? "<out_trade_no>" + reqData.getOutTradeNo() + "</out_trade_no>" : "<transaction_id>" + reqData.getTransactionId() + "</transaction_id>";
	   String xml = "<xml>" + "<appid>" + reqData.getAppid() + "</appid>" + "<mch_id>" + reqData.getMchId() + "</mch_id>" + "<nonce_str>" + nonceStr + "</nonce_str>" + trade + "<sign>" + sign + "</sign>" + "</xml>";
	   return post(queryOrder, xml, charset);
	}

	// 查询订单是否已经付款
	public boolean queryOrder(WxPayReqData reqData, int hospital) {
	   boolean result = false;
	   String json = _queryOrder(reqData);
	   if (checkSign(json)) {
		   Map<String, Object> map = XmlUtil.dom2Map(XmlUtil.parse(json));
		   String tradeState = Tools.getDesc(map.get("trade_state") + "");
		   String transaction_id = "";
		   result = "NOTPAY".equals(tradeState) || "PAYERROR".equals(tradeState); // 未付款、付款失败 返回true表示可以付款
		   if (!result) {
			   	List<Transaction> transactions = QBuilder.db("basedb").select().from("Transaction").where(new Where("outTradeNo=", reqData.getOutTradeNo()).and("status=", STATUS_ENABLE).and("hospital=", hospital)).query().getResultObj();
				if (transactions.size() == 0) {
					return result;
				}
				Integer payStatus = null;
				Integer state = null;
			    if ("SUCCESS".equals(tradeState)) {  // 已经成功付款
				     payStatus = ALREADY_PAY;
					   transaction_id = Tools.getDesc(map.get("transaction_id") + "", "");
			    } else if ("REFUND".equals(tradeState)) {  // 退款
				     payStatus = BACK_PAY;
					 state = STATE_HANDLED_PASS;
			    } else if ("CLOSED".equals(tradeState)) {  // 关闭
				     payStatus = PASS_PAY;
					 state = STATE_HANDLED_PASS;
			    }
				for (int i = 0; i < transactions.size(); i++) {
					Transaction transaction = transactions.get(i);
					transaction.setPayStatus(payStatus);
					if (ALREADY_PAY.equals(payStatus)) {
						transaction.setTransactionId(transaction_id);
					}
				}
				DBTools.db("basedb").modItem(transactions);
				// 查询订单的信息
				Transaction transaction = transactions.get(0);
				List<Order> orders = QBuilder.db("basedb").select().from("Order").where(new Where("id=", transaction.getOrderId()).and("status=", STATUS_ENABLE).and("hospital=", hospital)).query().getResultObj();
				if (orders.size() == 0 || !STATE_HANDLED_PASS.equals(state)) {
					return result;
				}
				Order order = orders.get(0);
				order.setState(STATE_HANDLED_PASS);
				DBTools.db("basedb").modItem(order, order.getId());
		   }
	   }
	   return result;
	}

	// 关闭订单
	public boolean closeOrder(WxPayReqData reqData) {
	   boolean result = false;
	   String nonceStr = getNonceStr();
	   reqData.setNonceStr(nonceStr);
	   String sign = getSign(reqData);
	   String xml = "<xml>" + "<appid>" + reqData.getAppid() + "</appid>" + "<mch_id>" + reqData.getMchId() + "</mch_id>" + "<nonce_str>" + nonceStr + "</nonce_str>" + "<out_trade_no>" + reqData.getOutTradeNo() + "</out_trade_no>" + "<sign>" + sign + "</sign>" + "</xml>";
	   String json = post(queryOrder, xml, charset);
	   if (checkSign(json)) {
		   Map<String, Object> map = XmlUtil.dom2Map(XmlUtil.parse(json));
		   String resultCode = Tools.getDesc(map.get("result_code") + "");
		   result = "SUCCESS".equals(resultCode);
	   }
	   return result;
	}

	// 下载账单
	public String downloadbill(WxPayReqData reqData, int hospital) {
		String nonceStr = getNonceStr();
	    reqData.setNonceStr(nonceStr);
	    String sign = getSign(reqData);
		String xml = "<xml>" + "<appid>" + reqData.getAppid() + "</appid>" + "<bill_date>" + reqData.getBillDate() + "</bill_date><bill_type>" + reqData.getBillType() + "</bill_type><mch_id>" + reqData.getMchId() + "</mch_id>" + "<nonce_str>" + nonceStr + "</nonce_str>" + "<sign>" + sign + "</sign>" + "</xml>";
        String json = post(downloadbill, xml, charset);
		return json;
	}

	public HashMap<String, String> getPrepayId(WxPayReqData reqData) {
		HashMap<String, String> PrepayMap = new HashMap<String, String> ();
		String nonceStr = getNonceStr();
		reqData.setNonceStr(nonceStr);
		String sign = getSign(reqData);
		String xml = "<xml>" + "<appid>" + reqData.getAppid() + "</appid>" + "<mch_id>" + reqData.getMchId() + "</mch_id>"
			+ "<nonce_str>" + nonceStr + "</nonce_str>" + "<sign>" + sign + "</sign>"
			+ "<body><![CDATA[" + reqData.getBody() + "]]></body>" + "<out_trade_no>" + reqData.getOutTradeNo() + "</out_trade_no>"
			+ "<attach>" + reqData.getAttach() + "</attach>" + "<total_fee>" + reqData.getTotalFee() + "</total_fee>"
			+ "<spbill_create_ip>" + reqData.getSpbillCreateIp() + "</spbill_create_ip>" + "<time_start>" + reqData.getTimeStart() + "</time_start>" + "<time_expire>" + reqData.getTimeExpire() + "</time_expire>" + "<notify_url>" + reqData.getNotifyUrl() + "</notify_url>" + "<trade_type>" + reqData.getTradeType() + "</trade_type>" + "<openid>" + reqData.getOpenid() + "</openid>" + "</xml>";
		String json = post(createOrder, xml, charset);
		System.out.println( "appid"+reqData.getAppid() );
		System.out.println( "mch_id"+reqData.getMchId() );
		System.out.println(xml);
		System.out.println("test3:"+json);
 		if (checkSign(json)) {
			 Map<String, Object> map = XmlUtil.dom2Map(XmlUtil.parse(json));
			 String prepay_id = Tools.getDesc(map.get("prepay_id") + "", "");
			 PrepayMap.put("prepay_id", prepay_id);
			 PrepayMap.put("out_trade_no", reqData.getOutTradeNo());
			 PrepayMap.put("out_trade_no", reqData.getOutTradeNo());
			 return PrepayMap;
		}
		PrepayMap.put("return_msg",json);

		return PrepayMap;
	}

  	public String getPackage(WxPayReqData reqData, int hospital, int count) {
		HashMap<String, String> PrepayMap = getPrepayId(reqData);

		String prepayId = PrepayMap.get("prepay_id");
		String out_trade_no = PrepayMap.get("out_trade_no");
		if(prepayId == null || prepayId.equals("")){
		    System.out.println( PrepayMap.get("return_msg"));
			return   PrepayMap.get("return_msg");
		}
		//String signType = "MD5";
		String result = "";
		System.out.println("test2:"+prepayId);
		if (!"".equals(prepayId)) {
			SortedMap<String, String> param = new TreeMap<String, String>();
			String nonceStr = getNonceStr();
			String timeStamp = getTimeStamp();
			String packages = "prepay_id=" + prepayId;
			if ("APP".equals(reqData.getTradeType())) {
				param.put("partnerid", reqData.getMchId());
				param.put("prepayid", prepayId);
				param.put("appid", reqData.getAppid());
				param.put("timestamp", timeStamp);
				param.put("noncestr", nonceStr);
				packages = "Sign=WXPay";
			} else {
				param.put("appId", reqData.getAppid());
				param.put("timeStamp", timeStamp);
				param.put("nonceStr", nonceStr);
				param.put("signType", signType);
			}
			param.put("package", packages);
			String sign = getSign(param);
			result = "{\"appId\":\"" + appId + "\",\"timeStamp\":\"" + timeStamp + "\",\"nonceStr\":\"" + nonceStr + "\",\"package\":\"" + packages + "\",\"signType\":\"" + signType + "\",\"paySign\":\"" + sign + "\", \"out_trade_no\":\"" + out_trade_no + "\", \"prepayId\":\"" + prepayId + "\", \"partnerId\":\"" + reqData.getMchId() + "\"}";
			System.out.println("test1:"+result);
		} else {
			WxPayReqData _reqData = new WxPayReqData();
			_reqData.setAppid(reqData.getAppid());
			_reqData.setMchId(reqData.getMchId());
			_reqData.setOutTradeNo(reqData.getOutTradeNo());
			out_trade_no = getOrder(4);

			List<Transaction> transactions = QBuilder.db("basedb").select().from("Transaction").where(new Where("outTradeNo=", reqData.getOutTradeNo()).and("status=", STATUS_ENABLE).and("payStatus=", WAIT_PAY).and("hospital=", hospital)).query().getResultObj();
			if (transactions.size() > 0) {
				for (int i = 0; i < transactions.size(); i++) {
					Transaction transaction = transactions.get(i);
					transaction.setOutTradeNo(out_trade_no);
				}
				DBTools.db("basedb").modItem(transactions);
			}
			reqData.setOutTradeNo(out_trade_no);
			++ count;
			if (count < 3) {
				result = getPackage(reqData, hospital, count);
			}
		}
		return result;
	}

	class SSLClient extends DefaultHttpClient {
		public SSLClient() throws Exception {
			SSLContext ctx = SSLContext.getInstance("TLS");
			X509TrustManager tm = new X509TrustManager() {
				public void checkClientTrusted(X509Certificate[] chain,
						String authType) throws CertificateException {
				}

				public void checkServerTrusted(X509Certificate[] chain,
						String authType) throws CertificateException {
				}

				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			};
			ctx.init(null, new TrustManager[] { tm }, null);
			SSLSocketFactory ssf = new SSLSocketFactory(ctx,
					SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
			ClientConnectionManager ccm = getConnectionManager();
			SchemeRegistry sr = ccm.getSchemeRegistry();
			sr.register(new Scheme("https", 443, ssf));
		}
	}

	public String post(String url, Map<String, String> map, String charset)
	  {
	    HttpClient httpClient = null;
	    HttpPost httpPost = null;
	    String result = null;
	    try {
	      httpClient = new SSLClient();
	      httpPost = new HttpPost(url);
	      List list = new ArrayList();
	      Iterator iter = map.entrySet().iterator();
	      while (iter.hasNext()) {
	        Map.Entry elem = (Map.Entry)iter.next();
	        list.add(new BasicNameValuePair((String)elem.getKey(), (String)elem.getValue()));
	      }
	      if (list.size() > 0) {
	        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, charset);
	        httpPost.setEntity(entity);
	      }
	      HttpResponse response = httpClient.execute(httpPost);
	      if (response != null) {
	    	  HttpEntity resEntity = response.getEntity();
	          if (resEntity != null)
	            result = EntityUtils.toString(resEntity, charset);
	      }
	    }
	    catch (Exception ex)
	    {
	      ex.printStackTrace();
	    }
	    return result;
	  }

	 public String post(String url, String xml, String charset) {
	    HttpClient httpClient = null;
	    HttpPost httpPost = null;
	    String result = null;
	    try {
	      httpClient = new SSLClient();
	      httpPost = new HttpPost(url);

	      if (xml != null) {
	        httpPost.setEntity(new StringEntity(xml, charset));
	      }
	      HttpResponse response = httpClient.execute(httpPost);
	      if (response != null) {
	        HttpEntity resEntity = response.getEntity();
	        if (resEntity != null)
	          result = EntityUtils.toString(resEntity, charset);
	      }
	    }
	    catch (Exception ex) {
	      ex.printStackTrace();
	    }
	    return result;
	 }

	 public Map<String, String> hospitalInfo(int hospital) {
		 // 医院信息
		List list = QBuilder.db("basedb").queryHql("from hospital where hospital =" + hospital + "");
		String json = "{}";
		if (list.size () > 0) {
			Map map = (Map)list.get(0);
			json = map.get("json") + "";
		}
		return GsonUtil.fromJson(json, new TypeToken<Map<String, String>>(){});
	 }

	 // 下单
	 public String packageInfo(Map<String, String> param, int hospital, int rlength) {  // 随机数长度
		WxPayReqData reqData = WxPayReqData.fromMap(param, WxPayReqData.class);
 		String trade_type = reqData.getTradeType();
		Map<String, String> hospitalMap = hospitalInfo(hospital);
		partnerKey = hospitalMap.get("partner_key");
		if ("JSAPI".equals(trade_type)) {
			appId = hospitalMap.get("wx_appid");
			mchId = hospitalMap.get("mch_id");
			notifyUrl = hospitalMap.get("notify_url");
		} else {
			appId = hospitalMap.get("appid");
			mchId = hospitalMap.get("app_mch_id");
			reqData.setOpenid("");
			notifyUrl = hospitalMap.get("notify_url");
		}
		reqData.setAppid(appId);
		reqData.setMchId(mchId);
		reqData.setNotifyUrl(notifyUrl);
		if ("".equals(Tools.getDesc(reqData.getOutTradeNo(), ""))) {  // 如果没有订单号传入则随机生成
			String outTradeNo = getOrder(rlength);
			reqData.setOutTradeNo(outTradeNo);
		}
		return getPackage(reqData, hospital, 0);
	 }

	 // 查询订单
	 public boolean orderInfo(Map<String, String> param, int hospital) {
		WxPayReqData reqData = WxPayReqData.fromMap(param, WxPayReqData.class);
  	String trade_type = reqData.getTradeType();
		Map<String, String> hospitalMap = hospitalInfo(hospital);
		partnerKey = hospitalMap.get("partner_key");
		String from = Tools.getDesc(param.get("from"), "wechat");
		if ("JSAPI".equals(trade_type)) {
			appId = hospitalMap.get("wx_appid");
			mchId = hospitalMap.get("mch_id");
		} else {
			appId = hospitalMap.get("appid");
			mchId = hospitalMap.get("app_mch_id");
			reqData.setOpenid("");
		}
		reqData.setAppid(appId);
		reqData.setMchId(mchId);
		return queryOrder(reqData, hospital);
	 }

	 // 查询订单号
	 public String orderInfoByOutTradeNo(Map<String, String> param, int hospital) {
		WxPayReqData reqData = WxPayReqData.fromMap(param, WxPayReqData.class);
  	String trade_type = reqData.getTradeType();
		Map<String, String> hospitalMap = hospitalInfo(hospital);
		partnerKey = hospitalMap.get("partner_key");
		String from = Tools.getDesc(param.get("from"), "wechat");
		if ("JSAPI".equals(trade_type)) {
			appId = hospitalMap.get("wx_appid");
			mchId = hospitalMap.get("mch_id");
		} else {
			appId = hospitalMap.get("appid");
			mchId = hospitalMap.get("app_mch_id");
			reqData.setOpenid("");
		}
		reqData.setAppid(appId);
		reqData.setMchId(mchId);
		return _queryOrder(reqData);
	 }

	 // 下载账单
	 public String billInfo(Map<String, String> param, int hospital) {
		WxPayReqData reqData = WxPayReqData.fromMap(param, WxPayReqData.class);
   	String trade_type = reqData.getTradeType();
		if ("".equals(trade_type)) {
			trade_type = "JSAPI";
		}
		Map<String, String> hospitalMap = hospitalInfo(hospital);
		partnerKey = hospitalMap.get("partner_key");
		if ("JSAPI".equals(trade_type)) {
			appId = hospitalMap.get("wx_appid");
			mchId = hospitalMap.get("mch_id");
		} else {
			appId = hospitalMap.get("appid");
			mchId = hospitalMap.get("app_mch_id");
			reqData.setOpenid("");
		}
		reqData.setAppid(appId);
		reqData.setMchId(mchId);
		String date = param.get("date");
		String type = param.get("type");
		reqData.setBillDate((date == null || "".equals(date)) ? DateUtil.formatDateByFormat(new Date(), "yyyyMMdd") : date);
		reqData.setBillType((type == null || "".equals(type)) ? "ALL" : type);
		return downloadbill(reqData, hospital);
	 }
%>
